#pragma once

#include <setjmp.h>
#include <string.h>

#include "dua.h"

typedef struct DuaObj DuaObj;
struct DuaObj{
    int type;
    DuaObj * gcNext; \
    unsigned ref;
    void (*free)(DuaObj* obj);
};
typedef struct DuaProto DuaProto;

typedef struct DuaString DuaString;
struct DuaString{
    DuaObj obj;
    int flag;
    size_t len;
    uint32_t hash;
    DuaString* next;
    char* s;
};
typedef struct {
    DuaObj obj;
    unsigned cap;
    unsigned count;
    DuaString** slot;
}DuaStrMap;

typedef struct DuaClosure DuaClosure;
typedef struct{
    union{
        int b;
        int32_t i;
        int64_t l;
        float f;
        double d;
        DuaString* ts;
        void* p;
        DuaClosure* closure;
    };
    int type;
}DuaValue;

typedef struct {
    int tk;
    DuaValue sem;
}DuaToken;

struct DuaClosure{
    DuaObj obj;
    DuaProto* proto;
    DuaValue** up;
    unsigned nUp;
};

typedef struct {
    void* ud;
    DuaAlloc alloc;
    DuaState* main;
    DuaStrMap* strmap;
}DuaVM;

struct DuaState{
    DuaObj obj;
    DuaVM* vm;
    jmp_buf jb;
    DuaValue* slot;
    unsigned cap;
    unsigned top;
    DuaState * parent;
};


#define duaCheckAlloc(D, P) \
    if(!(P)){ duaRet((D), DUA_NO_MEM, "no memory"); }

extern void*
duaMalloc(DuaState* D, size_t sz);
extern void*
duaRelloc(DuaState* D, void* p, size_t osz, size_t nsz);
extern void
duaFree(DuaState* D, void* p, size_t sz);
extern void*
duaNewObj(DuaState* D, int type, size_t sz);

extern DuaState*
duaNewStack(DuaState* D);

extern void
duaDo(DuaState* D, DuaClosure* clo);

extern int
duaEqualTs(DuaString* a, DuaString* b);

extern void
duaInitString(DuaState* D);
extern void
duaCloseString(DuaState* D);
extern DuaString*
duaNewStringLen(DuaState* D, const char* str, size_t len);
#define duaNewString(D, S) duaNewStringLen(D, (S), strlen(S))
extern char*
duaToNumber(const char* str, DuaValue* sem);

extern void
duaRet(DuaState* D, int ret, const char* msg);

